package student.productions;

import java.util.Random;

import student.InvalidSyntaxException;


public class Sensor extends MutableNode {
	// Invariant for children: if type is FOOD or DAMAGE, children is empty.
	// If type is NEARBY or AHEAD, children has one Term.
	
	public static final int NEARBY	= 1;
	public static final int AHEAD 	= 2;
	public static final int DAMAGE	= 3;
	public static final int FOOD 	= 4;
	
	private int type; // Type of sensor. Only has one type.
	
	/**
	 * Constructs a new Sensor
	 * @param st Type of Sensor to construct
	 * @param t Term that the Sensor operates on
	 * Checks: if t is NEARBY or AHEAD, this Sensor has a Term.
	 */
	public Sensor(int st, Term t){
		super();
		if ((st == NEARBY) || (st == AHEAD)) {
			if (t == null) {
				throw new InvalidSyntaxException("NEARBY and AHEAD types must have a Term");
			}
			children.add(t);
		}
		type = st;
	}
	
	/**
	 * Constructs a new Sensor of type FOOD or DAMAGE
	 * Checks: st is not NEARBY or AHEAD
	 * @param st Type of Sensor to construct
	 * @throws IllegalArgumentException if st is not FOOD or DAMAGE
	 */
	public Sensor(int st){
		this(st, null);
	}

	@Override
	public void prettyPrint(StringBuffer sb) {
		switch(type){
		case DAMAGE: sb.append("damage "); break;
		case FOOD: sb.append("food "); break;
		case NEARBY:
			sb.append("nearby[");
			children.get(0).prettyPrint(sb);
			sb.deleteCharAt(sb.length() - 1);//removes the space before the ]
			sb.append("] ");
			break;
		case AHEAD:
			sb.append("ahead[");
			children.get(0).prettyPrint(sb);
			sb.deleteCharAt(sb.length() - 1);//removes the space before the ]
			sb.append("] ");
		}
	}

	@Override
	public void mutate(Program genome) {
		StringBuffer sb = new StringBuffer();
		prettyPrint(sb);
		sb.append("was changed to ");
		Random rnd = new Random();
		if (type == FOOD || type == DAMAGE) { // Mutates by switching types.
			int newType;
			do {
				newType = rnd.nextInt(4) + 1;
			} while (type == newType);
			type = newType;
			if (type == AHEAD || type == NEARBY) {
				children.add(AbstractNode.generateTerm(genome, null));
			}
		} else { // type is AHEAD or NEARBY
			int mutationType = rnd.nextInt(3);
			if (mutationType == 0) { // Mutates by switching types.
				int newType;
				do {
					newType = rnd.nextInt(4) + 1;
				} while (type == newType);
				type = newType;
				if (type == FOOD || type == DAMAGE) children.clear();
			} else { // Replace Term with randomly generated Term.
				Term newTerm = AbstractNode.generateTerm(genome, children.get(0));
				children.set(0, newTerm);
			}
		}
		prettyPrint(sb);
		System.out.println(sb);
	}

	@Override
	public Node duplicate() {
		if (type == FOOD || type == DAMAGE) {
			return new Sensor(type);
		} else { // type is AHEAD or NEARBY. children has one Term.
			Term child= (Term)children.get(0).duplicate();
			return new Sensor(type, child);
		}
	}

	/**
	 * Checks if this Sensor is the same as n
	 * @param n the node to check against
	 * @return true if n is a Sensor and its type is the same as this one's
	 */
	@Override
	public boolean equals(Node n){
		if(!(n instanceof Sensor))
			return false;
		Sensor sNode = (Sensor) n;
		if(type != sNode.type)
			return false;
		if(type == AHEAD || type == DAMAGE)//if this Sensor has children and everything else lines up, check the children
			return super.equals(sNode);
		return true;//type is the same and no children, so true
	}
}
